package com.binarycat.dynamiclimit.collector;


import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.web.method.HandlerMethod;

import com.binarycat.dynamiclimit.DynamicLimiterManager;
import com.binarycat.dynamiclimit.annotation.DynamicLimit;
import com.binarycat.dynamiclimit.constant.CommonLimitConstants;
import com.binarycat.dynamiclimit.initialization.MetricFactory;
import com.binarycat.dynamiclimit.metric.SlidingHistory;
import com.binarycat.dynamiclimit.util.CommonLimitUtils;
import com.binarycat.util.jvm.SystemInfoUtils;
import com.codahale.metrics.Meter;
import com.codahale.metrics.Metric;
import com.codahale.metrics.MetricRegistry;

/**
 * 默认的限流信息收集器
 * 
 * @author jjb
 * @date 2018/08/06
 */
public class DefaultLimitDataCollector extends AbstractLimitDataCollector {
    private static String loclkey = CommonLimitUtils.getLock(DefaultLimitDataCollector.class);

    @Override
    public void preCollectMethodExecutionInformation(HttpServletRequest request, Object handler,
        DynamicLimiterManager dynamicLimiterManager) {
        if (handler instanceof HandlerMethod) {
            HandlerMethod method = (HandlerMethod)handler;
            request.setAttribute("dynamicLimit_method_startTime", System.currentTimeMillis());
            Metric[] qps = getMetric(request, dynamicLimiterManager, method, CommonLimitConstants.METRIC_QPS);
            if (qps == null) {
                return;
            }
            ((Meter)(qps[0])).mark();
            if (qps[1] != null) {
                ((Meter)qps[1]).mark();
            }
        }
        // 执行扩展的收集器方法
        AbstractLimitDataCollector limitDataCollectorExtend =
            dynamicLimiterManager.getDynamicLimiterConfig().getLimitDataCollectorExtend();
        if (limitDataCollectorExtend != null) {
            limitDataCollectorExtend.preCollectMethodExecutionInformation(request, handler, dynamicLimiterManager);
        }

    }

    private Metric[] getMetric(HttpServletRequest request, DynamicLimiterManager dynamicLimiterManager,
        HandlerMethod method, String key) {
        MetricRegistry metricRegistry = dynamicLimiterManager.getLimitData().getMetricRegistry();
        DynamicLimit dynamicLimit = CommonLimitUtils.getDynamicLimitAnnotation(method);

        // 没有限流注解
        if (dynamicLimit == null) {
            return null;
        }

        Map<String, Metric> map = metricRegistry.getMetrics();

        Metric metric0 = map.get(MetricRegistry.name(method.toString(), key));
        if (metric0 == null) {
            return null;
        }
        Metric[] metrics = new Metric[2];
        metrics[0] = metric0;

        if (dynamicLimit.limitByIp()) {
            Metric metric = map.get(MetricRegistry.name(CommonLimitUtils.getKeyByIpQuarantine(method, request), key));
            if (metric == null) {
                synchronized (loclkey) {
                    metric = map.get(MetricRegistry.name(CommonLimitUtils.getKeyByIpQuarantine(method, request), key));
                    if (metric == null) {
                        MetricFactory.registryMetric(dynamicLimiterManager.getLimitData(), method, request);
                    }
                }

            }
            Metric metric1 = map.get(MetricRegistry.name(CommonLimitUtils.getKeyByIpQuarantine(method, request), key));
            metrics[1] = metric1;
        }
        return metrics;

    }

    @Override
    public void postCollectMethodExecutionInformation(HttpServletRequest request, Object handler, Exception ex,
        DynamicLimiterManager dynamicLimiterManager) {
        if (handler instanceof HandlerMethod) {
            HandlerMethod method = (HandlerMethod)handler;

            Long startTimeMillis = (Long)request.getAttribute("dynamicLimit_method_startTime");
            Long endTimeMillis = System.currentTimeMillis();

            Metric[] timeoutSlidingHistorys =
                getMetric(request, dynamicLimiterManager, method, CommonLimitConstants.METRIC_TIMEOUT);

            Metric[] exceptionSlidingHistorys =
                getMetric(request, dynamicLimiterManager, method, CommonLimitConstants.METRIC_EXCEPTION);

            if (timeoutSlidingHistorys != null) {
                ((SlidingHistory)timeoutSlidingHistorys[0]).addData(0.00 + endTimeMillis - startTimeMillis);
                if (timeoutSlidingHistorys[1] != null) {
                    ((SlidingHistory)timeoutSlidingHistorys[1]).addData(0.00 + endTimeMillis - startTimeMillis);
                }
            }

            if (exceptionSlidingHistorys != null) {
                List<Class<? extends Throwable>> list =
                    dynamicLimiterManager.getDynamicLimiterConfig().getSpecificExceptions();

                if (ex == null || !containException(ex, list)) {
                    // 执行正常
                    ((SlidingHistory)exceptionSlidingHistorys[0]).addData(0);
                    if (exceptionSlidingHistorys[1] != null) {
                        ((SlidingHistory)exceptionSlidingHistorys[1]).addData(0);
                    }
                } else {
                    ((SlidingHistory)exceptionSlidingHistorys[0]).addData(1);
                    if (exceptionSlidingHistorys[1] != null) {
                        ((SlidingHistory)exceptionSlidingHistorys[1]).addData(1);
                    }
                }

            }

        }
        // 执行扩展的收集器方法
        AbstractLimitDataCollector limitDataCollectorExtend =
            dynamicLimiterManager.getDynamicLimiterConfig().getLimitDataCollectorExtend();
        if (limitDataCollectorExtend != null) {
            limitDataCollectorExtend.postCollectMethodExecutionInformation(request, handler, ex, dynamicLimiterManager);
        }
    }

    /**
     * 指定的异常是否包含于异常列表中
     * 
     * @param ex
     * @param list
     * @return true 包含
     */
    private boolean containException(Exception ex, List<Class<? extends Throwable>> list) {
        for (Class<? extends Throwable> throwableClass : list) {
            if (throwableClass.isInstance(ex)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public void collectSystemRuntimeInfo(DynamicLimiterManager dynamicLimiterManager) {
        MetricRegistry metricRegistry = dynamicLimiterManager.getLimitData().getMetricRegistry();

        // 收集内存使用率
        Map<String, Metric> metricMap = metricRegistry.getMetrics();
        SlidingHistory slidingHistory = (SlidingHistory)metricMap.get(CommonLimitConstants.METRIC_AVERAGE_MEMORY);
        slidingHistory.addData(SystemInfoUtils.getJvmMemoryUsageRate());

        // 执行扩展的收集器方法
        AbstractLimitDataCollector limitDataCollectorExtend =
            dynamicLimiterManager.getDynamicLimiterConfig().getLimitDataCollectorExtend();
        if (limitDataCollectorExtend != null) {
            limitDataCollectorExtend.collectSystemRuntimeInfo(dynamicLimiterManager);
        }

    }

}
